#include "StdAfx.h"
#include <MyFC/GridControl.h>

namespace My {

// Column

Column::Column() : 
	m_pColumns(0), m_Index(-1), m_Visible(true)
{
}

int Column::GetVisibleIndex() const
{
	return m_Visible ? int(RangeItem::GetIndex()) : -1;
}

void Column::SetVisible(bool on)
{
	if (m_Visible != on) {
		if (GetColumns() != 0)
			GetColumns()->SetColumnVisible(* this, on);
		else
			m_Visible = on;
	}
}

void Column::SetWidth(Size s)
{
	RangeItem::Resize(s);
}

Size Column::CalcVisibleIndex() const
{
	if (GetColumns() == 0)
		return 0;
	if (RangeItem::GetIndex() != -1)
		return My::Size(RangeItem::GetIndex());
	else {
		assert(m_Index != -1);
		// search for the next visible column
		for (My::Size i = My::Size(m_Index + 1); i < GetColumns()->GetColumnCount(); ++i) {
			int index = GetColumns()->GetColumn(i).GetVisibleIndex();
			if (index != -1)
				return My::Size(index);
		}
		// if none, then it should be the last
		return GetColumns()->GetVisibleColumnCount();
	}
}

// ColumnSet

ColumnSet::ColumnSet(Grid & grid) :
	m_Grid(grid)
{
}

bool ColumnSet::IsEmpty() const
{
	return m_Columns.empty();
}

Size ColumnSet::GetColumnCount() const
{
	return m_Columns.size();
}

Column & ColumnSet::GetColumn(Size i) const
{
	assert(i < m_Columns.size());
	return * m_Columns[i];
}

Size ColumnSet::GetVisibleColumnCount() const
{
	return Range::GetItemCount();
}

Column & ColumnSet::GetVisibleColumn(Size i) const
{
	return static_cast<Column &>(Range::GetItem(i));
}

Size ColumnSet::GetTotalWidth() const
{
	return Range::GetSize();
}

void ColumnSet::Insert(Size pos, Column * pColumn)
{
	assert(pos <= m_Columns.size());
	m_Columns.insert(m_Columns.begin() + pos, pColumn);
	pColumn->m_pColumns = this;
	pColumn->m_Index = pos;
	if (pColumn->m_Visible)
		Range::Insert(pColumn->CalcVisibleIndex(), pColumn);
}

void ColumnSet::Remove(Column * pColumn)
{
	assert(pColumn->GetColumns() == this);
	if (pColumn->m_Visible)
		Range::Remove(Size(pColumn->GetVisibleIndex()));
	m_Columns.erase(m_Columns.begin() + pColumn->m_Index);
	pColumn->m_pColumns = 0;
	pColumn->m_Index = -1;
}

void ColumnSet::Remove(Size pos, Size count)
{
	if (count == 0)
		return;
	assert(pos + count <= m_Columns.size());
	for (Size i = pos + count; i > pos; --i)
		Remove(m_Columns[i - 1]);
}

void ColumnSet::Clear()
{
	Range::Clear();
	for (Size i = 0; i < m_Columns.size(); ++i) {
		Column * pColumn = m_Columns[i];
		pColumn->m_pColumns = 0;
		pColumn->m_Index = -1;
	}
	m_Columns.clear();
}

void ColumnSet::InvalidateSpan(Span s)
{
	CRect r = m_Grid.ClientRect();
	r.left = s.first;
	r.right = s.last;
	m_Grid.InvalidateRect(r);
}

void ColumnSet::SizeChanged()
{
	m_Grid.UpdateContentSize();
}

void ColumnSet::SetColumnVisible(Column & col, bool on)
{
	assert(col.GetColumns() == this);
	if (col.m_Visible != on) {
		Size index = col.CalcVisibleIndex();
		if (!col.m_Visible)
			Range::Insert(index, &col);
		else
			Range::Remove(index);
		col.m_Visible = on;
	}
}

// Grid

Grid::Grid(ScrollBox & box) :
	List(box),
	m_Cols(* this)
{
}

My::Size Grid::GetPreferredWidth() const
{
	return m_Cols.GetTotalWidth();
}

// GridRow

Grid * GridRow::GetGrid() const
{
	if (GetList() == 0)
		return 0;
	assert(dynamic_cast<Grid *>(GetList()) != 0);
	return dynamic_cast<Grid *>(GetList());
}

void GridRow::HandlePaint(CDC * pDC, const CRect & r)
{
	if (GetGrid() == 0)
		return;
	PaintCells(pDC, r, GetGrid()->GetColumns());
}

void GridRow::PaintCells(CDC * pDC, const CRect & rClip, const ColumnSet & cols)
{
	for (My::Size i = 0; i < cols.GetVisibleColumnCount(); ++i) {
		Column & col = cols.GetVisibleColumn(i);
		CRect rCell = GetCellBounds(col);
		CRect rCellClip;
		rCellClip.IntersectRect(&rCell, &rClip);
		if (rCellClip.IsRectEmpty())
			continue;
		PaintCell(pDC, rCellClip, col, rCell); 
	}
}

CRect GridRow::GetCellBounds(const Column & col) const
{
	CRect rClient = ClientRect();
	CRect rCell(rClient);
	Span s = col.GetBounds();
	rCell.left = s.first;
	rCell.right = s.last;
	return rCell;
}

void GridRow::PaintCell(CDC * pDC, const CRect & rClip, const Column &, const CRect & rCell)
{
	CPen gray(PS_SOLID, 1, GetSysColor(COLOR_BTNSHADOW));
	pDC->MoveTo(rCell.left, rCell.bottom - 1);
	pDC->LineTo(rCell.right - 1, rCell.bottom - 1);
	pDC->LineTo(rCell.right - 1, rCell.top - 1);
}

// Cell

CRect Cell::Bounds() const
{
	if (GetColumn() == 0 || GetRow() == 0)
		return CRect(0, 0, 0, 0);
	return GetRow()->GetCellBounds(* GetColumn());
}

bool Cell::Visible() const
{
	if (GetColumn() == 0 || GetRow() == 0)
		return false;
	return GetColumn()->IsVisible();
}

GridRow * Cell::GetRow() const
{
	if (Parent() == 0)
		return 0;
	assert(dynamic_cast<GridRow *>(Parent()) != 0);
	return static_cast<GridRow *>(Parent());
}

// GridHeader

GridHeader::GridHeader()
{
	SetRowHeight(18);
}

void GridHeader::HandlePaint(CDC * pDC, const CRect & rClip)
{
	CBrush b(GetSysColor(COLOR_BTNFACE));
	pDC->FillRect(&rClip, &b);
	// Paint cells
	GridRow::HandlePaint(pDC, rClip);
	CRect r = ClientRect();
	r.left = GetGrid() != 0 ? GetGrid()->GetColumns().GetTotalWidth() : 0;
	r.right++;
	pDC->Draw3dRect(&r, GetSysColor(COLOR_BTNHILIGHT), GetSysColor(COLOR_BTNSHADOW));
}

void GridHeader::PaintCell(CDC * pDC, const CRect &, const Column & col, const CRect & rCell)
{
	pDC->Draw3dRect(&rCell, GetSysColor(COLOR_BTNHILIGHT), GetSysColor(COLOR_BTNSHADOW));
	String s = col.GetCaption();
	CRect r(rCell);
	r.InflateRect(-2, -2);
	if (!s.empty())
		pDC->DrawText(s.c_str(), &r, DT_SINGLELINE|DT_END_ELLIPSIS|DT_LEFT|DT_VCENTER);
}

} // My